package threadlocal;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 使用ThreadLocal要注意的点
 *
 * @author <a href='https://gitee.com/hzf2281'>codefish</a>
 * @version 1.0.0
 * @since 2022/08/25 上午 11:30
 */
public class ThreadLocalProblemTest {


    /**
     * ThreadLocalMap中的Entry的key对ThreadLocal的引用为弱引用的原因
     */
    @Test
    public void function(){
        //方法被调用，生成栈帧并入栈-----

        //这里对threadLocal对象引用为强引用
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        //set()方法使得在当前线程的threadLocalMap中形成一个Entry对象
        //这个Entry的对象结构为(key= 栈帧内的threadLocal对象 , value= "abc" )
        threadLocal.set("abc");
        System.out.println(threadLocal.get());

        //方法退出，这个栈帧出栈(被销毁)----------

        //如果Entry对key的引用为强引用，如果main线程没有被销毁，那么这个Entry就一直存在，
        // Entry对threadLocal对象的强引用导致这个threadLocal对象不能被gc。
        // 而将key的引用设置为弱引用就能解决这个问题，JVM进行GC时已失去强引用的threadLocal对象也会被立刻回收。
    }


    /**
     * 问题1：线程复用时，不及时清除变量副本导致后续业务出现问题
     */
    @Test
    public void problem1Test() throws Exception {
        //线程池线程数为3
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        ThreadLocal<Integer> resource = ThreadLocal.withInitial(() -> 0);
        //提交10个任务，每个任务从resource获取的值都应该时独立的
        for (int i = 0; i < 10; i++) {
            threadPool.submit(() -> {
                try{
                    int oldVal = resource.get();
                    resource.set(oldVal + 1);
                    System.out.println(Thread.currentThread().getName() + ":" + "旧值为 " + oldVal + ",新值为 " + resource.get());
                }finally {
                    resource.remove();
                }

            });
        }
         try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
    }
}
